//    Copyright (C) Kherty.  All rights reserved.
#region

using System;
using System.Diagnostics;
using System.Diagnostics.Contracts;

#endregion

namespace OpenLS.Spreadsheet
{
    public struct CellAddress
    {
        private readonly int _column;
        private readonly int _row;

        public CellAddress(Cell c): this (c.Row.Index, c.Column.Index)
        {
        }
        public override string ToString()
        {
            return Label;
        }

        public CellAddress(int row, int column)
        {
            Contract.Requires(row >= 0);
            Contract.Requires(column >= 0);
            Contract.Requires(row <= Worksheet.MaxRowIndex);
            Contract.Requires(column <= Worksheet.MaxColumnIndex);
            _row = row;
            _column = column;
        }

        public string Label
        {
            get { return ColumnConverter.IndexToName(Column) + (Row + 1); }
        }

        public int Row
        {
            get
            {
                Contract.Ensures(Contract.Result<int>() >= 0);
                return _row;
            }
        }

        public int Column
        {
            get
            {
                Contract.Ensures(Contract.Result<int>() >= 0);
                return _column;
            }
        }

        public static CellRelativeAddress operator -(CellAddress a, CellAddress b)
        {
            return new CellRelativeAddress(a.Row - b.Row, a.Column - b.Column);
        }

        public static bool operator ==(CellAddress a, CellAddress b)
        {
            return a.Row == b.Row && a.Column == b.Column;
        }

        public static bool operator !=(CellAddress a, CellAddress b)
        {
            return !(a == b);
        }

        public static CellAddress operator +(CellAddress a, CellRelativeAddress b)
        {
            Contract.Requires(a.Row + b.Row >= 0);
            Contract.Requires(a.Column + b.Column >= 0);

            return new CellAddress(a.Row + b.Row, a.Column + b.Column);
        }

        public static CellAddress operator -(CellAddress a, CellRelativeAddress b)
        {
            return new CellAddress(a.Row - b.Row, a.Column - b.Column);
        }

        public static CellAddress operator +(CellAddress a, CellSpan b)
        {
            return new CellAddress(a.Row + b.Row, a.Column + b.Column);
        }

        internal CellAddress GetNeighbor(NavigationDirection d)
        {
            var row = Row;
            var column = Column;
            switch (d)
            {
                case NavigationDirection.Up:
                    row--;
                    if (row < 0)
                        row = 0;
                    break;
                case NavigationDirection.Down:
                    row++;
                    break;
                case NavigationDirection.Left:
                    column--;
                    if (column < 0) column = 0;
                    break;
                case NavigationDirection.Right:
                    column++;
                    break;
            }
            if (row >= 0 && row <=  Worksheet.MaxRowIndex && column >= 0 && column <= Worksheet.MaxColumnIndex)
                return new CellAddress(row, column);
            return this;
        }

        internal bool IsAtEnd(NavigationDirection d)
        {
            switch (d)
            {
                case NavigationDirection.Up:
                    return _row == 0;
                case NavigationDirection.Left:
                    return _column == 0;
                case NavigationDirection.Right:
                    return false; //\\
                case NavigationDirection.Down:
                    return false; //\\
                default:
                    throw new NotSupportedException();
            }
        }

        internal RangeAddress ToRange()
        {
            return new RangeAddress(this, this);
        }

        public static CellAddress Parse(string s)
        {
            var column = 0;
            for (var i = 0; i < s.Length; i++)
            {
                var c = s[i];
                if (!char.IsLetter(c))
                {
                    var rowName = s.Substring(i, s.Length - i);
                    var row = int.Parse(rowName) - 1;
                    return new CellAddress(row, column);
                }
                if (i > 0)
                {
                    column = (column + 1)*26;
                }
                column = column + (c - 'A');
            }
            throw new InvalidOperationException();
        }

        internal static int PartialParsedColumn
            (string s)
        {
            var column = 0;
            for (var i = 0; i < s.Length; i++)
            {
                var c = s[i];
                if (!char.IsLetter(c))
                {
                    return column;
                }
                if (i > 0)
                {
                    column = (column + 1)*26;
                }
                column = column + (c - 'A');
            }
            throw new InvalidOperationException();
        }

        public static bool TryParse(string s, out CellAddress result)
        {
            var oneBasedColumn = 0;
            for (var i = 0; i < s.Length; i++)
            {
                var c = s[i];
                if (!char.IsLetter(c))
                {
                    var rowName = s.Substring(i, s.Length - i);
                    int row;
                    var b = int.TryParse(rowName, out row);
                    if (!b)
                    {
                        result = new CellAddress();
                        return false;
                    }
                    row--;
                    result = new CellAddress(row, oneBasedColumn-1);
                    return true;
                }
                oneBasedColumn = oneBasedColumn*26;
                oneBasedColumn = oneBasedColumn + (c - 'A')+1;
            }
            result = new CellAddress();
            return false;
        }

        public static int ColumnFromName(string s)
        {
            var column = 0;
            for (var i = 0; i < s.Length; i++)
            {
                var c = s[i];
                if (!char.IsLetter(c))
                {
                    return -1;
                }
                column = column*26;
                column = column + (c - 'A');
            }
            return column;
        }

        public override int GetHashCode()
        {
            return CellAddressComparer.GetHashCodeOf(this);
        }

        public override bool Equals(object obj)
        {
            return Equals((CellAddress) obj);
        }

        public bool Equals(CellAddress other)
        {
            return other.Row == Row && other.Column == Column;
        }
    }
}